package de.mmis.core.base.abstracttree;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import de.mmis.core.base.DeserializableAnnotations.DeserializationMethod;
import de.mmis.core.base.abstracttree.Tree.Type;
import de.mmis.core.base.abstracttree.serializer.DeserializableException;
import de.mmis.core.base.abstracttree.serializer.Serializable;
import de.mmis.core.base.abstracttree.serializer.selector.InstanceSelector;
import de.mmis.core.base.abstracttree.serializer.selector.SimpleSelector;
import de.mmis.core.base.gclass.GClass;

public class CStruct implements Serializable {

	protected final Map<String, Object> propertyMap;

	public CStruct() {
		this.propertyMap = new HashMap<String, Object>();
	}

	public Object get(String key) {
		return this.propertyMap.get(key);
	}

	public void set(String key, Object value) {
		this.propertyMap.put(key, value);
	}

	@Override
	public String getTypeIdentifier() {
		return "struct";
	}

	@DeserializationMethod
	public static InstanceSelector deserialize(Tree[] exps,
			ClassLoader classLoader, String id, Map<String, Object> idmap)
			throws DeserializableException {
		return deserializeProperties(new CStruct(), exps, classLoader, id,
				idmap);
	}

	public static InstanceSelector deserializeProperties(CStruct cStruct,
			Tree[] exps, ClassLoader classLoader, String id,
			Map<String, Object> idmap) throws DeserializableException {

		InstanceSelector ret = new SimpleSelector(cStruct, id, idmap);

		for (Tree exp : exps) {
			if (exp.getType() != Type.INNER
					|| ((InnerNode) exp).getNumberOfSubTrees() != 2)
				throw new DeserializableException(new InnerNode(exps),
						"property " + exp + " is no 2-elem struct");
			Tree keyexp = ((InnerNode) exp).getSubTree(0);
			Tree valexp = ((InnerNode) exp).getSubTree(1);
			if (keyexp.getType() != Type.LEAF
					|| !((LeafNode) keyexp).getUTF8Data().startsWith(":"))
				throw new DeserializableException(new InnerNode(exps),
						"property " + exp
								+ ": key must be an atom starting with ':'");
			String key = ((LeafNode) keyexp).getUTF8Data().substring(1);
			Object value = valexp.getAs(GClass.fromClass(Object.class),
					classLoader, idmap);

			cStruct.set(key, value);
		}

		return ret;
	}

	@Override
	public Tree[] serialize(boolean explicit, Map<Object, String> refmap,
			ClassLoader classLoader) {
		Tree[] ret = new Tree[this.propertyMap.size()];
		int i = 0;
		for (Entry<String, Object> entry : this.propertyMap.entrySet()) {
			ret[i++] = new InnerNode(new LeafNode(":" + entry.getKey()),
					Tree.fromObject(entry.getValue(), explicit, refmap,
							classLoader));
		}
		return ret;
	}
}
